﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Text;
using System.Xml;
using NLog;
using SsoaFramework.Core.Other;

namespace SsoaFramework.Core
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class ExposeServiceBus : IExposeServiceBus, ISilverlightPolicyProvider
    {
        ServiceHost service;
        ServiceHost silverlightPolicyService;
        readonly EnterpriseServiceBus serviceBus;

        public ExposeServiceBus(EnterpriseServiceBus serviceBus)
        {
            this.serviceBus = serviceBus;
        }

        public void Start()
        {
            Log.Instance.Info("Exposing ESB starting");
            Expose();
            ProvideSilverlightPolicy();
        }

        private void Expose()
        {
            service = new ServiceHost(this, new Uri(serviceBus.ServiceRouter.Address + "Router/"));

            service.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = false });
            service.AddServiceEndpoint(
                typeof(IMetadataExchange),
                MetadataExchangeBindings.CreateMexHttpBinding(),
                "mex");
            bool isDebugBehaviorExist = service.Description.Behaviors.Any(b => b.GetType() == typeof(ServiceDebugBehavior));
            if (isDebugBehaviorExist)
                service.Description.Behaviors.Find<ServiceDebugBehavior>().IncludeExceptionDetailInFaults = true;
            else
                service.Description.Behaviors.Add(new ServiceDebugBehavior() { IncludeExceptionDetailInFaults = true });

            service.AddServiceEndpoint(typeof(IExposeServiceBus), new PollingDuplexHttpBinding(PollingDuplexHttpSecurityMode.None), "");
            service.Open();
        }

        private void ProvideSilverlightPolicy()
        {
            silverlightPolicyService = new ServiceHost(this, serviceBus.ServiceRouter.Address);
            ServiceEndpoint policyEndpoint = silverlightPolicyService.AddServiceEndpoint(typeof(ISilverlightPolicyProvider), new WebHttpBinding(), String.Empty);
            policyEndpoint.Behaviors.Add(new WebHttpBehavior());
            silverlightPolicyService.Open();   
        }

        public void Stop()
        {
            Log.Instance.Info("Exposing ESB stopping");
            service.Close();
            silverlightPolicyService.Close();
        }

        #region IExposeServiceBus Members
        public IServiceBusClient ClientEndpoint { get; private set; }
        public void EstablishConnection()
        {
            Log.Instance.Info("Connection to ESB established");
            ClientEndpoint = OperationContext.Current.GetCallbackChannel<IServiceBusClient>();
        }

        IEnumerable<Service> IExposeServiceBus.GetServices()
        {
            return serviceBus.ServiceRegistry.Services;
        }

        public IEnumerable<Service> GetAllServices()
        {
            return serviceBus.ServiceRegistry.Services;
        }

        public IEnumerable<Service> GetRoutedServices()
        {
            return serviceBus.ServiceRouter.RoutingTable.GetRoutedServices();
        }

        public void RouteService(Service service)
        {
            serviceBus.ServiceRouter.RoutingTable.Route(service);
        }

        public void UnRouteService(Service service)
        {
            serviceBus.ServiceRouter.RoutingTable.Unroute(service);
        }

        public void ChangeRoute(Service service, string path)
        {
            serviceBus.ServiceRouter.RoutingTable.ChangeRoute(service, path);
        }

        public Service GetService(Uri serviceAddress)
        {
            return serviceBus.ServiceRegistry.Services.First(s => s.ServicePath.ToString()==serviceAddress.ToString());
        }

        #endregion

        #region ISilverlightPolicyProvider Members
        public Message ProvidePolicyFile()
        {
            string xml =
                @"<?xml version=""1.0"" encoding=""utf-8""?>
<access-policy>
    <cross-domain-access>
        <policy>
            <allow-from http-request-headers=""*"">
                <domain uri=""*""/>
            </allow-from>
            <grant-to>
                <resource path=""/"" include-subpaths=""true""/>
            </grant-to>
        </policy>
    </cross-domain-access>
</access-policy>";

            XmlReader reader = XmlReader.Create(StringToStream(xml));
            Message result = Message.CreateMessage(MessageVersion.None, "", reader);
            return result;
        }
        Stream StringToStream(string result)
        {
            return new MemoryStream(Encoding.UTF8.GetBytes(result));
        }
        #endregion
    }
}
